package io.kwdb.sdk.internal;

import io.kwdb.sdk.Config;
import io.kwdb.sdk.Point;
import java.util.List;

/**
 * Utility class for validating input parameters. Provides validation methods for points,
 * configurations, and other inputs.
 */
public final class Validators {

  // Private constructor to prevent instantiation
  private Validators() {
    throw new AssertionError("Validators is a utility class and should not be instantiated");
  }

  /**
   * Validate a single point.
   *
   * @param point The point to validate
   * @throws IllegalArgumentException if the point is invalid
   */
  public static void validatePoint(Point point) {
    if (point == null) {
      throw new IllegalArgumentException("Point cannot be null");
    }

    if (point.getMeasurement() == null || point.getMeasurement().isEmpty()) {
      throw new IllegalArgumentException("Measurement cannot be null or empty");
    }

    if (point.getFields().isEmpty()) {
      throw new IllegalArgumentException("Point must have at least one field");
    }

    // Validate field keys
    for (String key : point.getFields().keySet()) {
      if (key == null || key.isEmpty()) {
        throw new IllegalArgumentException("Field key cannot be null or empty");
      }
    }

    // Validate tag keys and values
    for (var entry : point.getTags().entrySet()) {
      if (entry.getKey() == null || entry.getKey().isEmpty()) {
        throw new IllegalArgumentException("Tag key cannot be null or empty");
      }
      if (entry.getValue() == null) {
        throw new IllegalArgumentException("Tag value cannot be null");
      }
    }

    // Validate timestamp if present
    if (point.hasTimestamp() && point.getTimestamp() < 0) {
      throw new IllegalArgumentException("Timestamp cannot be negative");
    }
  }

  /**
   * Validate a batch of points.
   *
   * @param batch The batch of points to validate
   * @throws IllegalArgumentException if the batch or any point is invalid
   */
  public static void validateBatch(List<Point> batch) {
    if (batch == null) {
      throw new IllegalArgumentException("Batch cannot be null");
    }

    if (batch.isEmpty()) {
      throw new IllegalArgumentException("Batch cannot be empty");
    }

    // Validate each point
    for (int i = 0; i < batch.size(); i++) {
      try {
        validatePoint(batch.get(i));
      } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException(
            "Invalid point at index " + i + ": " + e.getMessage(), e);
      }
    }
  }

  /**
   * Validate a SQL query string.
   *
   * @param sql The SQL query to validate
   * @throws IllegalArgumentException if the SQL is invalid
   */
  public static void validateSql(String sql) {
    if (sql == null) {
      throw new IllegalArgumentException("SQL query cannot be null");
    }

    if (sql.trim().isEmpty()) {
      throw new IllegalArgumentException("SQL query cannot be empty");
    }

    // Basic SQL injection prevention (very basic, real implementation should be more sophisticated)
    if (containsSuspiciousPattern(sql)) {
      throw new IllegalArgumentException("SQL query contains potentially dangerous patterns");
    }
  }

  /**
   * Check for suspicious SQL patterns. This is a basic check and should not be relied upon for
   * security. Real SQL injection prevention should be done server-side.
   *
   * @param sql The SQL to check
   * @return true if suspicious patterns are found
   */
  private static boolean containsSuspiciousPattern(String sql) {
    String lower = sql.toLowerCase();

    // Check for multiple statements
    if (sql.contains(";") && !sql.trim().endsWith(";")) {
      return true;
    }

    // Check for common SQL injection patterns (very basic)
    String[] dangerousPatterns = {
      "--", // SQL comment
      "/*", // Multi-line comment start
      "*/", // Multi-line comment end
      "xp_", // SQL Server extended procedures
      "sp_", // SQL Server stored procedures
      "0x", // Hex encoding
      "\\x" // Hex encoding alternative
    };

    for (String pattern : dangerousPatterns) {
      if (lower.contains(pattern)) {
        return true;
      }
    }

    return false;
  }

  /**
   * Validate a configuration object.
   *
   * @param config The configuration to validate
   * @throws IllegalArgumentException if the configuration is invalid
   */
  public static void validateConfig(Config config) {
    if (config == null) {
      throw new IllegalArgumentException("Config cannot be null");
    }

    if (config.getHost() == null || config.getHost().isEmpty()) {
      throw new IllegalArgumentException("Host cannot be null or empty");
    }

    if (config.getPort() < 1 || config.getPort() > 65535) {
      throw new IllegalArgumentException("Port must be between 1 and 65535");
    }

    if (config.getTimeoutMillis() < 0) {
      throw new IllegalArgumentException("Timeout cannot be negative");
    }

    if (config.getBatchSize() < 1) {
      throw new IllegalArgumentException("Batch size must be at least 1");
    }

    if (config.getQueueCapacity() < 1) {
      throw new IllegalArgumentException("Queue capacity must be at least 1");
    }
  }

  /**
   * Check if a point is valid without throwing an exception.
   *
   * @param point The point to check
   * @return true if the point is valid, false otherwise
   */
  public static boolean isValidPoint(Point point) {
    try {
      validatePoint(point);
      return true;
    } catch (IllegalArgumentException e) {
      return false;
    }
  }

  /**
   * Check if a batch is valid without throwing an exception.
   *
   * @param batch The batch to check
   * @return true if the batch is valid, false otherwise
   */
  public static boolean isValidBatch(List<Point> batch) {
    try {
      validateBatch(batch);
      return true;
    } catch (IllegalArgumentException e) {
      return false;
    }
  }

  /**
   * Check if a SQL query is valid without throwing an exception.
   *
   * @param sql The SQL to check
   * @return true if the SQL is valid, false otherwise
   */
  public static boolean isValidSql(String sql) {
    try {
      validateSql(sql);
      return true;
    } catch (IllegalArgumentException e) {
      return false;
    }
  }
}
